<script setup>
import {handlePaste} from '@/utils/inputUtils.js';
import EmojiPicker from '@/components/emoji/EmojiPicker.vue';
import {reactive, ref, watch, onBeforeMount} from "vue";
import {useRoute} from "vue-router";
import {MessageFactory} from "@/socket/SocketMessage.js";
import {getConversations} from "@/apis/chat/conversation.js";
import {getMessages} from "@/apis/chat/message.js";
import {userInfoStore} from "@/stores/user.js";
import {useSocketStore} from "@/stores/socket.js";
import {getGroupInfo} from "@/apis/chat/group.js";
import {getUserInfoAPI} from "@/apis/user.js";
import {uploadFile as uploadFileApi, uploadFiles} from "@/apis/chat/file.js";
import {formatSimpleDateTime, } from "@/utils/common.js";
import {getProductById} from "@/apis/product.js";
import {ElMessage} from "element-plus";
// 用户信息管理
const user = userInfoStore()
// Socket连接管理
const socketStore = useSocketStore()
// 会话列表备份
const conversationsBackup = ref([])

onBeforeMount(async () => {
  await fetchConversations();
})

socketStore.$subscribe((mutation, state) => {
  if (state.messages.length > 0) {

    const data = state.messages[0]
    state.messages.splice(0, 1);
    console.log('listening', data)
    if (data.conversationId == current.id && data.sender !== id.value)
      records.value.push(data)
    // 当前在群聊窗口，直接标记已读
    if (data.conversationId == current.id && current.conversation.type === 'GROUP') {
      socketStore.clearUnread(data.conversationId, user.userInfo.userId)
    }

    updateLastMessage(data)
  }
})
// 当前群聊信息
const groupInfo = reactive({
  data: {}
});
// 当前聊天的对方用户信息
const otherUser = reactive({
  data: {}
})
// 会话列表分页信息
const conversationPageSize = reactive({
  offset: 0,
  size: 100,
  total: 0
})
// 消息列表分页信息
const messagesPageSize = reactive({
  offset: 0,
  size: 1000,
  total: 0
})
// 路由信息
const route = useRoute();
// 当前用户id
const id = ref(user.userInfo.userId)
// 当前会话信息
const current = reactive({
  id : null,
  conversation: null,
  userId : null,
  lastMessage: null
})
const product = ref(null)
// 表情输入
const emojiShow = ref(false);
// 当前会话消息列表
const records = ref([]);
// 会话列表
const conversations = ref([]);
// 搜索
const searchInput = ref('');
// 搜索
const search = () => {
  searchInput.value = searchInput.value.trim();

  conversations.value = conversationsBackup
      .value
      .filter(item => item.conversation.name.includes(searchInput.value))
}

const userInfo = ref({});

// 拉取群聊信息
const fetchGroupInfo = (groupId) => {
  getGroupInfo(groupId).then((resp) => {
    console.log('members', resp.data)
    groupInfo.data = resp.data
    for (let item of resp.data.members)
      userInfo.value[item.id] = item
  })
}
// 拉取用户信息
const fetchUserInfo = (userId) => {
  getUserInfoAPI({userId: userId}).then((resp) => {
    console.log('userInfo', resp.data)
    otherUser.data = resp.data
    userInfo.value[userId] = resp.data
    console.log('userInfo', userInfo.value)
  })
}

const getAvatar = (sender) => {
  if (sender === user.userInfo.userId)
      return user.userInfo.avatarUrl
  if (current.conversation.type === 'GROUP')
      return groupInfo.data.members.find(item => item.id == sender).avatar;
  else
    return otherUser.data.avatarUrl
}
// 更新最新消息
const updateLastMessage = (message) => {
  if (message.conversationId !== current.id)
    increaseUnreadCount(message.conversationId)

  for (let item of conversations.value) {
    if (item.conversation.id === message.conversationId) {
      item.lastMessage = message;
      break;
    }
  }
}
// 更新未读消息数量
const increaseUnreadCount = (conversationId) => {
  for (let item of conversations.value) {
    if (item.conversation.id === conversationId) {
      item.unreadCount += 1;
    }
  }
}
// 监听消息列表变化，滚动到底部
watch(records, () => {
  setTimeout(() => {
    scrollViewToBottom();
  }, 50)
}, {deep : true})

// 拉取会话列表
const fetchConversations = async () => {
  console.log('Conversation UserID', id.value)

  const resp = await getConversations({
    userId : id.value,
    offset: conversationPageSize.offset,
    size: conversationPageSize.size,
  });

  conversations.value = resp.rows;
  conversationPageSize.total = resp.total;
  // 备份会话列表，用于搜索
  conversationsBackup.value = resp.rows;

  if (route.query.conversationId) {
    await clickConversation(conversations.value
        .find(item => item.conversation.id == route.query.conversationId))

    const response = await getProductById(route.query.productId)
    if (response.code === 200) {
      const inputDom = document.getElementById('richInput');
      inputDom.innerHTML = generateProductMessage(response.data);
    }
  }
}

function generateProductMessage(product) {
  const detail = product.products;
  let message = '商品名称：' + detail.title
   + '<br>商品价格：' + detail.price
    + '<br>商品描述：' + detail.description
    + '<br>商品标签：' + detail.tags;

  for (let i = 0; i < product.productImages.length && i <= 1; i ++) {
    const item = product.productImages[i].imageUrl;
    message +=  `<br><img class="msg-pic" src="${item}" style="margin-right: .5em;" alt='img'>`
  }

  return message;
}

// 解析最后一条消息
const parseLastMessage = (data) => {
  if (! data) return
  if (data.type === 'text') {
    return data.content.replace(/<img.*?>/g, '[图片]');
  } else if (data.type === 'file') {
    return JSON.parse(data.content).name;
  }
}

// 点击会话
const clickConversation = async (data) => {

  groupInfo.data = {}
  otherUser.data = {}
  current.conversation = data.conversation;
  current.id = data.conversation.id;
  current.lastMessage = data.lastMessage;
  current.userId = data.userId;

  if (data.conversation.type === 'GROUP') {
    fetchGroupInfo(data.conversation.id);
  } else {
    fetchUserInfo(data.userId);
  }

  for (let item of conversations.value) {
    if (item.conversation.id === data.conversation.id) {
      item.unreadCount = 0;
      break;
    }
  }

  const resp = await getMessages({
    conversationId: data.conversation.id,
    userId: id.value,
    offset: messagesPageSize.offset,
    size: messagesPageSize.size
  })

  messagesPageSize.total = resp.total;
  records.value = resp.rows;
}

// 选择表情
const selectEmoji = (index) => {
  const richInput = document.getElementById('richInput');
  if (document.activeElement !== richInput)
    richInput.focus();
  let emojiImage = `<img src='/emojis/emoji-${index}.svg' alt='emoji'>`
  richInput.innerHTML += emojiImage;
}
// 滚动到底部
const scrollViewToBottom = () => {
  const chatView = document.getElementById('chatView');
  chatView.scrollTop = chatView.scrollHeight;
}
const handleSendingMessage = (message) => {
  const isGroup = current.conversation.type === 'GROUP';

  message.receiver = isGroup ? current.conversation.id : current.userId;
  const data = message

  updateLastMessage(data)

  if (isGroup) {
    socketStore.sendMessageGroups(current.conversation.id, data)
  } else {
    socketStore.sendMessage(data)
  }

  records.value.push(data);
}
// 发送消息
const sendMessage = () => {

  const inputDom = document.getElementById('richInput');

  handleSendingMessage(MessageFactory.createTextMessage(
    current.conversation.id,
    id.value,
    null,
    inputDom.innerHTML
  ));


  inputDom.innerHTML = '';
}
// 上传图片
const uploadImage = () => {
  const imageUpload = document.getElementById('imageUpload');

  imageUpload.click();
  imageUpload.addEventListener('change', handleImageSelect);
}
// 上传文件
const uploadFile = () => {
  const fileUpload = document.getElementById('fileUpload');

  fileUpload.click();
  fileUpload.addEventListener('change', handleFileUpload);
}
// 上传文件
const handleFileUpload = (event) => {
  const file = event.target.files[0];

  let data = new FormData();
  data.append('file', file);
  ElMessage.info('文件上传中...')
    uploadFileApi(data)
      .then(resp => {

        handleSendingMessage(MessageFactory.createFileMessage(
            current.conversation.id,
            id.value,
            null,
            resp.data,
            file.name,
            file.size,
            file.type
        ))

        event.target.value = '';
      })
}
// 上传图片
const handleImageSelect = (event) => {
  console.log('here are files', event.target.files)
  const files = event.target.files;

  let data = new FormData();
  for (const file of files)
    data.append('files', file, file.name);
  ElMessage.info('图片上传中')
  uploadFiles(data)
      .then(resp => {
        const richInput = document.getElementById('richInput');

        if (document.activeElement !== richInput)
          richInput.focus();

        for (const item of resp.data) {
          let image = `<img class="msg-pic" src="${item}" style="margin-right: .5em;" alt='img'>`

          richInput.innerHTML += image;
        }

        event.target.value = '';
      })
}

// 点击图片
const clickContent = (e) => {
  if (e.target.className === 'msg-pic') {
    window.open(e.target.src, '_blank');
  }
}
// 计算文件大小
const calculateUnits = (size) => {
  if (size < 1024) {
    return `${size}B`;
  } else if (size < 1024 * 1024) {
    return `${(size / 1024).toFixed(2)}KB`;
  } else {
    return `${(size / 1024 / 1024).toFixed(2)}MB`;
  }
}

const getFileType = (content) => {
  const format = JSON.parse(content).format;
  if (! format)
    return 'unknown';
  return format.split('/')[0];
}

const downloadFile = (item) => {

  const fileInfo = JSON.parse(item.content)

// 使用 window.open 在新窗口/标签页中打开文件链接，尝试触发下载
  const downloadWindow = window.open(fileInfo.url, '_blank');
}

</script>

<template>
  <div class="self-container">
    <div class="user-list">
      <div class="search-box">
          <el-input
              size="large"
              clearable
              type="text"
              v-model="searchInput"
              @keyup.enter="search"
              @blur="search"
              placeholder="搜索会话"
          />
      </div>
      <div
          :class="current.id === item.conversation.id ? 'active' : ''"
          class="user-list-item"
          @click="clickConversation(item)"
          v-for="item in conversations"
      >
        <img :src="item.conversation.avatar" alt="avatar">
        <div class="user-text">
          <div class="last-time">
            {{ formatSimpleDateTime(item.lastMessage ?
              item.lastMessage.timestamp : item.conversation.createTime) }}
          </div>
          <p>{{ item.conversation.name }}</p>
          <span>{{ parseLastMessage(item.lastMessage) }} </span>
        </div>
        <div v-if="item.unreadCount > 0" class="unread">{{ item.unreadCount }}</div>
      </div>
    </div>
    <div class="chat-box">
      <div v-if="! current.id" class="no-user-content">
        <p>Please select a user to chat</p>
      </div>
      <div v-else class="chat-content">
        <div class="chat-header">
          <p>{{ current.conversation.name }}</p>
        </div>
        <div id="chatView" class="chat-body">
          <div
              v-for="item in records"
              class="record-content"
              :class="item.sender == id ? 'message-right' : 'message-left'">
            <img
                :src="getAvatar(item.sender)"
                alt="avatar">
            <div
                class="user-info"
                :class="item.sender == id ? 'info-right' : 'info-left'"
            >
              <span v-if="userInfo[item.sender]" class="info-name">{{ userInfo[item.sender].username }}</span>
              <span class="info-time">{{ formatSimpleDateTime(item.timestamp) }}</span>
            </div>
            <div class="message-box">
              <div
                  v-if="item.type === 'text'"
                  @click="clickContent"
                  v-html="item.content"
                  class="record-message">
              </div>
              <div
                  v-else-if="item.type === 'file'"
                  class="record-file"
                  @click="downloadFile(item)"
              >
                <div @click="clickContent" v-if="getFileType(item.content) === 'image'">
                  <img class="msg-pic" width="100%" :src="JSON.parse(item.content).url" alt="file">
                </div>
                <div v-else-if="getFileType(item.content) === 'video'" >
                  <video width="100%" controls>
                    <source :src="JSON.parse(item.content).url" type="video/mp4">
                  </video>
                </div>
                <div v-else>
                  <div class="file-info">
                    <p>{{ JSON.parse(item.content).name }}</p>
                    <span>{{ calculateUnits(JSON.parse(item.content).size) }}</span>
                  </div>
                  <img src="@/assets/images/file-icon.svg" alt="file">
                </div>
              </div>
            </div>
          </div>
        </div>

        <div class="chat-footer">
          <div class="function-btns">
            <div @click="emojiShow = !emojiShow" style="position: relative;">
              <div style="position: absolute; bottom: 100%">
                <EmojiPicker v-if="emojiShow" @selectEmoji="selectEmoji" />
              </div>
              <img src="@/assets/images/emoji.png" alt="emoji">
            </div>
            <div @click="uploadImage">
              <input type="file" multiple id="imageUpload" accept="image/*" style="display: none;" />
              <img src="@/assets/images/pic.png" alt="image">
            </div>
            <div @click="uploadFile">
              <input type="file" id="fileUpload" style="display: none;" />
              <img src="@/assets/images/file.png" alt="file">
            </div>

          </div>
          <div
              @paste="handlePaste"
              class="rich-input"
              id="richInput"
              contenteditable="true"
              spellcheck="false"
              autofocus
          >
          </div>
          <el-button
              type="primary"
              class="send-btn"
              @click="sendMessage"
          >发送</el-button>
        </div>
      </div>

    </div>
    <div v-if="groupInfo.data.groupName" class="chat-info">
      <span class="info-title">群聊信息</span>
      <div class="info-content">
        <div>{{ groupInfo.data.groupDesc }}</div>
      </div>
      <div class="info-members">
        <span class="info-title">群聊成员 ({{ groupInfo.data.members.length }})</span>
        <div class="member-item" v-for="item in groupInfo.data.members">
          <img :src="item.avatar" alt="avatar">
          <p> {{ item.username }}</p>
          <div v-if="groupInfo.data.groupOwner == item.id" class="owner-tag">群主</div>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped lang="scss">

img {
  background: transparent !important;
}
.self-container {
  height: 100%;
  display: flex;
}

.emoji {
  width: 1rem;
}

.user-list {
  width: 22%;
  overflow-y: auto;
  border-right: 1px solid #e0dcdc;
  background: #faf7f7;
}
.search-box {
  height: 3.5rem;
  position: sticky;
  top: 0;
  display: flex;
  align-items: center;
  padding: .5em;
}
.search-box > div {
  height: 2em;
}
.search-box input {
  width: 100%;
  height: 100%;
  padding: .2em .5em;
  outline: none;
  background-color: #cccccc;
}

.user-list-item {
  position: relative;
  display: flex;
  justify-content: start;
}

.active {
  cursor: pointer;
  background-color: #eaeaea;
}

.user-list-item:hover {
  cursor: pointer;
  background-color: #eaeaea;
}

.user-list-item > img {
  margin: 10px;
  width: 50px;
  height: 50px;
  border-radius: 50%;
}
.user-text {
  margin: 10px 0;
  width: 100%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;

  .last-time {
    float: right;
    color: #6a6a6a;
    font-size: 0.9em;
    margin-right: 1em;
    opacity: .8;
  }

  span {
    display: block;
    margin-top: .8em;
    font-size: 0.9em;
    color: #6a6a6a;
  }
}

.search {
  position: sticky;
  top: 0;
}
.chat-box {
  flex: 1;
}
.no-user-content {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

.no-user-content > img {
  width: 50%;
}

.chat-content {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.chat-header {
  height: 3.5rem;
  padding: 5px;
  border-bottom: 1px solid #b3b2b2;
}

.chat-body {
  flex: 1;
  overflow: auto;
}
.chat-info {
  width: 18%;
  height: 100%;
  background: white;
  padding: 1em;
}
.info-title {
  display: block;
  font-size: 1.2em;
  border-bottom: #6a6a6a 1px solid;
  width: 100%;
}
.info-content {
  word-break: break-word;
  overflow-y: auto;
  max-height: 30%;
}
.info-members {
  margin-top: 2rem;
  overflow-y: auto;
  max-height: 60%
}
.info-members .info-title {
  background: white;
  position: sticky;
  top: 0;
}
.member-item {
  padding: .5em;
}
.member-item:hover {
  cursor: pointer;
  background: #eaeaea;
}
.member-item:after {
  clear: both;
  content: "";
  display: block;
  width: 0;
  height: 0;
}
.member-item img{
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  float: left;
}
.member-item p {
  float: left;
  line-height: 2rem;
  margin-left: .5em;
}
.owner-tag {
  float: right;
  margin: .2em;
  padding: .1em .3em;
  border-radius: .2em;
  font-size: .85rem;
  background: gold;
}

.chat-footer {
  position: relative;
  border-top: 1px solid #b3b2b2;
  height: 30%;
}
.record-content {
  margin: .5em;
  display: flex;
  position: relative;
}

.message-right {
  flex-direction: row-reverse;
  justify-content: end;
}

.record-message {
  margin-top: 1.5em;
  max-width: 30rem;
  background-color: white;
  padding: .5em .75em;
  border-radius: .5em;
  overflow-wrap: break-word;
}

.record-content > img {
  width: 3rem;
  height: 3rem;
  margin: 0.5rem;
  border-radius: 50%;
}

.function-btns {
  display: flex;
}

.function-btns img {
  width: 2rem;
  margin: .5em;
}

.function-btns img:hover {
  cursor: pointer;
}

.rich-input {
  height: 60%;
  resize: none;
  overflow-y: auto;
  outline: none;
  margin-left: 1em;

  white-space: pre-wrap;      /* 保留空格和换行，自动换行 */
  word-wrap: break-word;      /* 长单词或无空格字符串强制换行 */
  word-break: break-all;      /* 允许在任意字符间断开（适合中英文混合） */
  overflow-wrap: break-word;  /* 同上，现代浏览器推荐使用 */
}

.rich-input >>> img {
  max-width: 120px;
}

.send-btn {
  position: absolute;
  right: 3em;
  bottom: 1em;
}

.record-message >>> .msg-pic {
  max-width: 100%;
}

.record-file {
  position: relative;
  background: white;
  border-radius: 1em;
  width: 15em;
  padding: 1em;
  margin-top: 1.5em;
}
.file-info {
  float: left;
  width: 8em;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}

.file-info span {
  margin-top: 1em;
  display: block;
  color: #908f8f;
}
.record-file::after {
  content: '';
  display: block;
  clear: both;
  width: 0;
  height: 0;
}
.record-file img {
  float: right;
}
.unread {
  position: absolute;
  top: .2em;
  right: .2em;
  background: red;
  border-radius: 50%;
  text-align: center;
  line-height: 1.25em;
  height: 1.25em;
  width: 1.25em;
  color: white;
}

.user-info {
  position: absolute;
  display: flex;
  color: #6a6a6a;
  font-size: .9em;
}

.info-time {
  margin: 0 .5em;
}
.info-left {
  left: 4.5em;
  top: 0;
}

.info-right {
  right: 4.5em;
  top: 0;
  flex-direction: row-reverse;
}

.download-btn {

  &:hover {
    cursor: pointer;
    background: #c2c1c1;
    border-radius: 1em;
  }
}

.down-left {
  position: absolute;
  bottom: 0;
  right: -1.5em;
}

.down-right {
  position: absolute;
  bottom: 0;
  left: -1.5em;
}

</style>
